/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

#include <bootparam.h>

#include <arch/arm/stddef.h>
#include <arch/arm/serial.h>
#include <stdbool.h>
#include <stdio.h>

/* The boot parameter string can contain parameters, and parameters
 * can contain key value pairs.
 *
 * Parameters are defined by: "parameter-name:parameter-value".  Subsequent
 * parameters are separated by spaces.
 *
 * The value of a parameter is a list of key value pairs, the entries
 * of this list are separated by commas.  An entry is defined by:
 * "key=value".
 *
 * An example boot parameter string is then:
 *   "rd0:addr=0x87000000,size=16777216 root:dev=rd0
 */
static const char bootparams[] =
  "rd0:addr=0x87000000,size=8425472 root:dev=rd0";

void
bootparam_init(void)
{
  /* FIXME: In the future, this should be "copied" from the boot
   * manager somehow.
   */

  early_puts("Boot arguments:");
  early_putc(' ');
  early_putc(' ');
  early_puts(bootparams);
  early_putlf();
}

const char *
bootparam_get_parameter(const char *key,
                        int        *len)
{
  int i;
  bool in_param = false;

  for (i = 0; bootparams[i] != 0; i++)
    {
      if (bootparams[i] == '\"')
        {
          if (!in_param)
            goto parse_error;

          /* Fast-forward to closing quote */
          while (bootparams[++i] != '\"' && bootparams[i] != 0)
            i++;

          continue;
        }
      else if (bootparams[i] == ':')
        {
          if (in_param)
            goto parse_error;

          in_param = true;

          continue;
        }
      else if (bootparams[i] == ' ')
        {
          /* Note that we cannot be in a quote here, that's handled
           * above.
           */
          in_param = false;

          continue;
        }

      if (!in_param)
        {
          int j;

          for (j = 0; bootparams[i+j] != 0 && key[j] != 0; j++)
            if (bootparams[i+j] != key[j])
              break;

          if (key[j] == 0 && bootparams[i+j] == ':')
            {
              int start = i + j + 1;

              /* Parameter found */
              if (len)
                {
                  for (i = start; bootparams[i] != 0; i++)
                    {
                      if (bootparams[i] == '\"')
                        {
                          while (bootparams[++i] != '\"' && bootparams[i] != 0)
                            i++;

                          continue;
                        }

                      if (bootparams[i] == ' ')
                        break;
                    }

                  *len = i - start;
                }

              return &bootparams[start];
            }

          i += j;
        }
    }

  return NULL;

parse_error:
  puts("Parse error in boot parameter string");
  return NULL;
}

const char *
bootparam_get_value(const char *key,
                    int        *key_len,
                    const char *parameter,
                    int         param_len)
{
  int i;
  bool in_value = false;

  for (i = 0; i < param_len; i++)
    {
      if (parameter[i] == '\"')
        {
          if (!in_value)
            goto parse_error;

          while (parameter[++i] != '\"' && i < param_len)
            i++;

          continue;
        }
      else if (parameter[i] == '=')
        {
          if (in_value)
            goto parse_error;

          in_value = true;

          continue;
        }
      else if (parameter[i] == ',')
        {
          in_value = false;

          continue;
        }

      if (!in_value)
        {
          int j;

          for (j = 0; i + j < param_len && key[j] != 0; j++)
            if (parameter[i+j] != key[j])
              break;

          if (key[j] == 0 && parameter[i+j] == '=')
            {
              int start = i + j + 1;

              /* Value found */
              if (key_len)
                {
                  for (i = start; i < param_len; i++)
                    {
                      if (parameter[i] == '\"')
                        {
                          while (parameter[++i] != '\"' && i < param_len)
                            i++;

                          continue;
                        }

                      if (parameter[i] == ',')
                        break;
                    }

                  *key_len = i - start;
                }

              return &parameter[start];
            }

          i += j;
        }
    }

  return NULL;

parse_error:
  puts("Parse error in given parameter");
  return NULL;
}
